﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Text;

namespace WebHost
{
    //服务行为
    public class MyServiceBehaviorAttribute : Attribute, IServiceBehavior
    {
        private string serviceType;
        public MyServiceBehaviorAttribute(string serviceType)
        {
            this.serviceType = serviceType;
        }
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (var endpoint in serviceDescription.Endpoints)
            {
                endpoint.Behaviors.Add(new MyEndpointBehavior(serviceType));
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            //Console.WriteLine("Inside {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
            return;
        }
    }

    //终结点行为
    public class MyEndpointBehavior : IEndpointBehavior
    {
        private string serviceType;
        public MyEndpointBehavior(string serviceType)
        {
            this.serviceType = serviceType;
        }
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            return;
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyMessageInspector());
            endpointDispatcher.DispatchRuntime.OperationSelector = new MyWebOperationSelector(endpoint, serviceType);
        }

        public void Validate(ServiceEndpoint endpoint)
        {
            return;
        }
    }

    //契约行为
    public class MyContractBehaviorAttribute : Attribute, IContractBehavior
    {
        public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            return;
        }

        public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
        {
            return;
        }

        public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
        {
            return;
        }
    }

    //操作行为
    public class MyOperationBehaviorAttribute : Attribute, IOperationBehavior
    {
        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
            return;
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            return;
        }

        public void Validate(OperationDescription operationDescription)
        {
            return;
        }
    }

    //消息拦截
    public class MyMessageInspector : IDispatchMessageInspector
    {
        object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            return null;
        }

        void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
        {
            WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
        }
    }

    public class MyWebOperationSelector : WebHttpDispatchOperationSelector
    {
        protected string serviceType;
        public MyWebOperationSelector(ServiceEndpoint endpoint) : base(endpoint)
        {
        }

        public MyWebOperationSelector(ServiceEndpoint endpoint, string serviceType) : base(endpoint)
        {
            this.serviceType = serviceType;
        }

        protected MyWebOperationSelector() : base()
        {
        }

        public new string SelectOperation(ref Message message)
        {
            return base.SelectOperation(ref message);
        }

        ///eliminate 307 redirect
        ///IgnoreTrailingSlash
        protected override string SelectOperation(ref Message message, out bool uriMatched)
        {
            string opName = base.SelectOperation(ref message, out uriMatched);
            if (!uriMatched)
            {
                string lastSegment = message.Headers.To.Segments.Last();
                if (lastSegment.Equals(serviceType, StringComparison.OrdinalIgnoreCase))
                {
                    string toUri = message.Headers.To.AbsoluteUri;
                    int questionmarkIndex = toUri.IndexOf('?');
                    if (questionmarkIndex != -1)
                    {
                        toUri = toUri.Insert(questionmarkIndex, "/");
                    }
                    else
                    {
                        toUri = toUri + "/";
                    }
                    message.Headers.To = new Uri(toUri);
                    opName = base.SelectOperation(ref message, out uriMatched);
                }
            }
            return opName;
        }
    }
}
